﻿// <copyright file="Program.cs" company="EDD Solutions Ltd.">
// Copyright (c) EDD Solutions Ltd. 2010.
// </copyright>
// <author>$Author$</author>
// <date>$Date$</date>
// <revision>$Revision$</revision>
namespace Sharepoint.Manipulator.Net.SvnHook.PreCommit
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Class representing a program to handle Subversion PreCommit hook requests.
    /// </summary>
    /// <remarks>Based on work by Troy Hunt for Subversion hooks in .NET</remarks>
    /// <see cref="http://www.troyhunt.com/2010/02/creating-subversion-pre-commit-hooks-in.html"/>
    public class Program
    {
        /// <summary>
        /// Program entry point
        /// </summary>
        /// <param name="args">the parameters passed to the program.</param>
        public static void Main(string[] args)
        {
            string repos = args[0];
            string txn = args[1];
            string format = "t";
            if (args.Length == 3)
            {
                format = args[2];
            }

            string log = GetSvnLookOutput(repos, format, txn, "log");
            string changedPaths = GetSvnLookOutput(repos, format, txn, "changed");

            int exitCode = 0;
            string logValidation = GetLogMessageErrors(log);
            if (logValidation != null)
            {
                Console.Error.WriteLine(logValidation);
                exitCode = 1;
            }

            string changedPathsValidation = GetFileNameErrors(changedPaths);
            if (changedPathsValidation != null)
            {
                Console.Error.WriteLine(changedPathsValidation);
                exitCode = 1;
            }

            if (args.Length == 3)
            {
                System.Console.ReadLine();
            }

            Environment.Exit(exitCode);
        }

        /// <summary>
        /// Call to SvnLook to identify transaction details.
        /// </summary>
        /// <param name="repos">the repository</param>
        /// <param name="numberFormat">r/t revision or transaction</param>
        /// <param name="number">the transaction/revision number</param>
        /// <param name="subcommand">the sub commmand</param>
        /// <returns>the svnlook generated string</returns>
        private static string GetSvnLookOutput(string repos, string numberFormat, string number, string subcommand)
        {
            ProcessStartInfo processStartInfo = new ProcessStartInfo
            {
                FileName = "svnlook.exe",
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                Arguments = String.Format("{0} -{1} \"{2}\" \"{3}\"", subcommand, numberFormat, number, repos)
            };

            Process process = Process.Start(processStartInfo);
            string output = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            return output;
        }

        /// <summary>
        /// Validate Log Messages
        /// </summary>
        /// <param name="log">the log message.</param>
        /// <returns>an error message or null if theare no errors.</returns>
        private static string GetLogMessageErrors(string log)
        {
            // check message length
            if (log.Length < 20)
            {
                return "Message is less than 20 characters.";
            }

            if (log.Split(' ').Length < 5)
            {
                return "Message is less than 5 words.";
            }

            // check for sharepoint task
            bool gotSharepointReference = false;
            string[] lines = log.ToLower().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {
                if (line.StartsWith("http"))
                {
                    IOperation exists = new Exists();
                    exists.ParseParameters(new string[] { line });
                    gotSharepointReference = (bool)exists.Run();
                }
            }

            if (!gotSharepointReference)
            {
                return "Message does not contain a valid Sharepoint Reference";
            }

            return null;
        }

        /// <summary>
        /// Validate the submitted files.
        /// </summary>
        /// <param name="changedPaths">the paths that have changed</param>
        /// <returns>an error message or null if theare no errors.</returns>
        private static string GetFileNameErrors(string changedPaths)
        {
            string[] changeRows = Regex.Split(changedPaths.TrimEnd(), Environment.NewLine);
            foreach (string changeRow in changeRows)
            {
                char changeType = changeRow[0];
                string filePath = changeRow.Substring(4, changeRow.Length - 4);
                string fileName = Path.GetFileName(filePath);
                string directoryName = Path.GetDirectoryName(filePath).ToLower();
                if (changeType != 'D' && fileName == "Thumbs.db")
                {
                    return "Thumbs.db file was found.";
                }

                if (changeType != 'D' && directoryName.Contains("/obj/"))
                {
                    return "*/obj/* directory file was found.";
                }

                if (changeType != 'D' && directoryName.Contains("/bin/"))
                {
                    return "*/bin/* directory file was found.";
                }
            }

            return null;
        }
    }
}
